#include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "rfs-defs.h"

int sd = 0;
char cwd[MAX_STRING_SIZE];
int isfirst = 1;
PACKAGE * package = NULL;

long rls(PACKAGE * package);
long rcd(PACKAGE * package);
long get(PACKAGE * package);
long put(PACKAGE * package);
int close_socket(int sd);
int open_socket(struct sockaddr_in dir);

void ls(void) {
    if (isfirst == 1) {
        isfirst = 0;
        getcwd(cwd, MAX_STRING_SIZE);
    }
    // estructuras mediante las cuales leeeremos el contenido del directorio
    DIR *dp1;
    struct dirent *dirp;

    // abrimos el directorio
    dp1 = opendir(cwd);

    // si no se puede abrir el directorio...
    if (!dp1) {
        printf("No se pudo leer el directorio.\n");
    } else {
        // leemos contenido del directorio y agregamos los nombres
        // de archivos o directorios al buffer, descartando el directorio
        // actual y el 'padre'
        while ((dirp = readdir(dp1)) != NULL) {
            if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
                continue;
            // si es un directorio, agrego [DIR] a la salida
            if (dirp->d_type == 0x004)
                printf("%s - [DIR]\n", dirp->d_name);
            else
                printf("%s\n", dirp->d_name);
        }
        closedir(dp1);
    }
}

void cd(char *argument) {
    // si es la primera vez que se ejecuta la función,
    // seteamos el directorio de trabajo actual
    if (isfirst == 1) {
        isfirst = 0;
        getcwd(cwd, MAX_STRING_SIZE);
    }

    if (chdir(argument) == 0) {
        getcwd(cwd, MAX_STRING_SIZE);
        printf("Se cambió correctamente el directorio.\nEl directorio actual es '%s'.\n", cwd);
    } else {
        printf("No se pudo cambiar de directorio.\nUd. intentó cambiar a '%s'\n", argument);
    }
}

void execute(char *command, char *argument, int argc) {

    if (package == NULL)
        package = (PACKAGE *) malloc(sizeof (PACKAGE));

    bzero(package->data, DATA_LENGTH);
    package->from = sd;
    //// RLS ////
    if (strcmp(command, "rls") == 0) {
        rls(package);
    } else
        //// RCD ////
        if (strcmp(command, "rcd") == 0) {
        if (argc < 3) {
            printf("Debe ingresar el directorio de destino.\n");
            return;
        }
        strncpy(package->data, argument, DATA_LENGTH);
        rcd(package);
    } else
        //// GET ////
        if (strcmp(command, "get") == 0) {
        if (argc < 3) {
            printf("Debe ingresar el archivo a descargar.\n");
            return;
        }
        strncpy(package->data, argument, DATA_LENGTH);
        get(package);
    } else
        //// PUT ////
        if (strcmp(command, "put") == 0) {
        if (argc < 3) {
            printf("Debe ingresar el archivo a subir.\n");
            return;
        }
        strncpy(package->data, argument, DATA_LENGTH);
        put(package);
    } else
        //// LS ////
        if (strcmp(command, "ls") == 0) {
        ls();
    } else

        //// CD ////
        if (strcmp(command, "cd") == 0) {
        if (argc < 3) {
            printf("Debe indicar el directorio.\n");
            return;
        }
        cd(argument);
    } else
        printf("Operando '%s' no reconocido.\nSolo se aceptan 'put', 'get', 'ls', rls', 'cd' y 'rcd'.\n", command);
}

void run_client() {
    char *input = (char *) malloc(MAX_STRING_SIZE);
    char *command = (char *) malloc(MAX_STRING_SIZE);
    char *argument = (char *) malloc(MAX_STRING_SIZE);
    int i, j, k, argc;
    printf(">>> ");
    while (strcmp(gets(input), "exit") != 0) {
        for (i = 0, j = 0, k = 0; i < strlen(input); i++) {
            if (input[i] == ' ' && (i == j)) {
                while (input[++i] == ' ');
                command[j] = '\0';
            }

            if (i == j)
                command[j++] = input[i];
            else
                argument[k++] = input[i];

        }
        if (strlen(argument) == 0)
            argc = 0;
        else
            argc = 3;
        if (k == 0)
            command[j] = '\0';
        else
            argument[k] = '\0';
        execute(command, argument, argc);

        printf(">>> ");
    }
}

int main(int argc, char** argv) {

    if (argc < 3) {
        printf("Uso: cliente <direccion>\n");
        printf("     Donde: <direccion> = <ip> <puerto>\n");
        exit(-1);
    }

    // Seteo la direccion y trato de conectarme. 
    bzero((char *) & dir, sizeof (dir));
    dir.sin_family = AF_INET;
    if (inet_pton(AF_INET, argv[1], &dir.sin_addr) <= 0) {
        perror("ERROR INTERPRETANDO DIRECCIÓN: ");
        exit(-1);
    }
    dir.sin_port = htons(atoi(argv[2]));

    if ((sd = open_socket(dir)) < 0) {
        perror("ERROR CONECTAR: ");
        exit(-1);
    }

    run_client();

    close_socket(sd);
    return (EXIT_SUCCESS);
}